package cn.demoncat.util.sec;

import cn.demoncat.util.io.CloseUtil;

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.SecureRandom;

/**
 * 基于TLS协议的KeyTool和OpenSSL证书密钥库
 */
public class HttpsUtil extends SecurityUtil{

	/**
	 * 加密协议（SSL或TLS）
	 */
	public static final String PROTOCOL = "TLS";
	
	/**
	 * 获取HTTPS连接：使用HttpURLConnection构建连接(发送请求，读取响应)，加密解密自动实现
	 * @注           需要先创建相应的密钥库和数字证书
	 * @param url
	 * @param keyStorePath 密钥库路径
	 * @param keyStorePwd 密钥库密码
	 * @param isBoth 是否为双向认证
	 * @param keyCode 信任库路径
	 * @param keyCode KEY_CODE(如果为null，生成随机密钥；如果不为null，生成固定密钥)
	 */
	public static HttpsURLConnection httpsConn(String url,String keyStorePath,String keyStorePwd,boolean isBoth, String keyCode)  {
		// 建立HTTPS连接
		HttpsURLConnection conn;
		try {
			conn = (HttpsURLConnection)new URL(url).openConnection();
		} catch (Exception e) {
			throw new RuntimeException("URL连接失败",e);
		}
		// 配置SSL连接工厂
		return configSSLSocketFactory(conn, keyStorePath, keyStorePwd, isBoth, keyCode);
	}
	
	/**
	 * 获得密钥库
	 * @param keyStorePath 密钥库路径
	 * @param keyStorePwd 密钥库密码
	 * @param isBoth 是否为双向认证
	 */
	private static KeyStore getKeyStore(String keyStorePath,String keyStorePwd,boolean isBoth)  {
		// 实例化密钥库
		KeyStore ks;
		try {
			if (isBoth) {
				// 双向认证使用PKCS12实例化密钥库
				ks = KeyStore.getInstance("PKCS12",PROVIDER);
			}else{
				// 单向认证使用默认方式实例化密钥库
				ks = KeyStore.getInstance(KeyStore.getDefaultType(),PROVIDER);
			}
		} catch (Exception e) {
			throw new RuntimeException("密钥库实例化失败",e);
		}
		// 获得密钥库文件
		FileInputStream is = null;
		try {
			is = new FileInputStream(keyStorePath);
			// 加载密钥库
			ks.load(is,keyStorePwd.toCharArray());
			return ks;
		} catch (Exception e) {
			CloseUtil.close(is);
			throw new RuntimeException("密钥库路径错误或密码错误",e);
		}
	}

	/**
	 * 获取SSL连接工厂
	 * @param keyStorePath 密钥库路径
	 * @param keyStorePwd 密钥库密码
	 * @param isBoth 是否为双向认证
	 * @param keyCode KEY_CODE(如果为null，生成随机密钥；如果不为null，生成固定密钥)
	 */
	private static SSLSocketFactory getSSLSocketFactory(String keyStorePath,String keyStorePwd,boolean isBoth, String keyCode)  {
		// 实例化密钥库
		KeyStore ks = getKeyStore(keyStorePath,keyStorePwd,isBoth);
		try {
			// 实例化密钥管理工厂
			KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm(),PROVIDER);
			// 初始化密钥管理工厂
			kmf.init(ks, keyStorePwd.toCharArray());
			// 实例化信任库管理工厂
			TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(),PROVIDER);
			// 初始化信任库管理工厂
			tmf.init(ks);
			// 实例化SSL上下文
			SSLContext ctx = SSLContext.getInstance(PROTOCOL,PROVIDER);
			// 安全随机数
			SecureRandom sRandom;
			if (keyCode == null) {
				// 随机成生密钥
				sRandom = new SecureRandom();
			}else{
				// SHA1密码随机数供应者，通过KEY_CODE生成固定的密钥
				sRandom = SecureRandom.getInstance("SHA1PRNG");
				sRandom.setSeed(keyCode.getBytes(StandardCharsets.UTF_8));
			}
			// 初始化SSL上下文
			ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), sRandom);
			// 获得连接工厂
			return ctx.getSocketFactory();
		} catch (Exception e) {
			throw new RuntimeException("密钥库路径错误或密码错误",e);
		}
	}
	
	/**
	 * 为HttpsURLConnection配置SSL连接工厂
	 * @param conn Https连接
	 * @param keyStorePath 密钥库路径
	 * @param keyStorePwd 密钥库密码
	 * @param isBoth 是否为双向认证
	 * @param keyCode KEY_CODE(如果为null，生成随机密钥；如果不为null，生成固定密钥)
	 */
	private static HttpsURLConnection configSSLSocketFactory(HttpsURLConnection conn,String keyStorePath,String keyStorePwd,boolean isBoth, String keyCode)  {
		// 获得SSL连接工厂
		SSLSocketFactory sslSocketFactory = getSSLSocketFactory(keyStorePath, keyStorePwd, isBoth, keyCode);
		// 设置SSL连接工厂
		conn.setSSLSocketFactory(sslSocketFactory);
		return conn;
	}
	
}
